

import ikelib.Crc;
import ikelib.byteUtil;
import ikelib.wintime;

import javax.swing.JPanel;

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.BorderLayout;
import java.awt.Point;

import javax.swing.DefaultListModel;
import javax.swing.JDialog;
import javax.swing.JTextField;
import javax.swing.JButton;
import javax.swing.BoxLayout;
import javax.swing.SwingUtilities;

import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeSet;
import java.util.Vector;

import javax.swing.JToggleButton;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.JCheckBox;
import javax.swing.JList;

import wcg.ActProtocolFrame;



public class WinSender extends JDialog {
	// WINA0 再送に対応
	// ACTは再送に未対応
	private static final String PropertyFile = "winA0sender.propertes";  //  @jve:decl-index=0:
	private static final String FmtWindowPosX = "Window position X";  //  @jve:decl-index=0:
	private static final String FmtWindowPosY = "Window position Y";  //  @jve:decl-index=0:
	private static final String FmtTarget = "Target";
	private static final int  DATAGENETATOR_MAX	= 30;

	private Crc CRCAct;  //  @jve:decl-index=0:
	private String 		TargetIp;
	private int			TargetPort;
	private int  SendCounter = 0;
	private long SendCounterLong = 0L;
	private wintime CurrentTime;  //  @jve:decl-index=0:
	
	private static final long serialVersionUID = 1L;
	private JPanel jContentPane = null;
	private JTextField jTextFieldTergetIP = null;
	private JToggleButton jToggleButtonSend = null;
	private JLabel jLabel = null;
	private JTextField jTextFieldMessage = null;
	private JScrollPane jScrollPane = null;
	private JTable jTable = null;
	//private DefaultTableModel TableModel=null;
	private String[] TableColumnName = {"enable","name","smpl","mode","amp","dc-ofs","freq","phase"};
//	private String[][] TableData = {
//			{"y","1001","100","0","100","0",	"1.0",	"0.0"},
//			{"y","1002","100","1","150","0",	"1.0",	"0.5"},
//			{"y","1003","100","2","100","0",	"0.5",	"1.0"},
//
//			{"y","1004","100","3","100","10",	"0.1",	"1.5"},
//			{"y","1005","100","4","100","0",	"0.1",	"2.5"},
//			{"y","1006","100","5","100","0",	"0.1",	"3.0"},
//			
//			{"y","2007","100","0","1000","0",	"1.0",	"0.0"},
//			{"y","2008","100","1","1500","0",	"1.0",	"0.5"},
//			{"y","2009","100","2","1000","0",	"0.5",	"1.0"},
//			
//			{"y","2010","100","3","1000","10",	"0.1",	"1.5"},
//			{"y","2011","100","4","1000","0",	"0.1",	"2.5"},
//			{"y","2012","100","5","1000","0",	"0.1",	"3.0"},
//
//			{"y","3013","100","0","1000","0",	"1.0",	"3.0"},
//			{"y","3014","100","0","1000","0",	"1.0",	"3.0"},
//			{"y","3015","100","0","1000","0",	"1.0",	"3.0"},
//			
//			{"y","3016","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","3017","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","3018","100","0","1000","0",	"0.1",	"3.0"},
//			
//			{"y","4019","100","0","1000","0",	"1.1",	"3.0"},
//			{"y","4020","100","0","1000","0",	"1.1",	"3.0"},
//			{"y","4021","100","0","1000","0",	"1.1",	"3.0"},
//
//			{"y","4022","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","4023","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","4024","100","0","1000","0",	"0.1",	"3.0"},
//			
//			{"y","5025","100","0","1000","0",	"1.1",	"3.0"},
//			{"y","5026","100","0","1000","0",	"1.1",	"3.0"},
//			{"y","5027","100","0","1000","0",	"1.1",	"3.0"},
//			
//			{"y","5028","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","5029","100","0","1000","0",	"0.1",	"3.0"},
//			{"y","5030","100","0","1000","0",	"1.1",	"3.0"},
//			
//	};
	private Object[][] TableData = {
		{new Boolean(true),"1001","100","0","100","0",	"1.0",	"0.0"},
		{new Boolean(true),"1002","100","1","150","0",	"1.0",	"0.5"},
		{new Boolean(true),"1003","100","2","100","0",	"0.5",	"1.0"},
	
		{new Boolean(true),"1004","100","3","100","10",	"0.1",	"1.5"},
		{new Boolean(true),"1005","100","4","100","0",	"0.1",	"2.5"},
		{new Boolean(true),"1006","100","5","100","0",	"0.1",	"3.0"},
		
		{new Boolean(true),"2007","100","0","1000","0",	"1.0",	"0.0"},
		{new Boolean(true),"2008","100","1","1500","0",	"1.0",	"0.5"},
		{new Boolean(true),"2009","100","2","1000","0",	"0.5",	"1.0"},
		
		{new Boolean(false),"2010","100","3","1000","10",	"0.1",	"1.5"},
		{new Boolean(false),"2011","100","4","1000","0",	"0.1",	"2.5"},
		{new Boolean(false),"2012","100","5","1000","0",	"0.1",	"3.0"},
	
		{new Boolean(false),"3013","100","0","1000","0",	"1.0",	"3.0"},
		{new Boolean(false),"3014","100","0","1000","0",	"1.0",	"3.0"},
		{new Boolean(false),"3015","100","0","1000","0",	"1.0",	"3.0"},
		
		{new Boolean(false),"3016","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"3017","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"3018","100","0","1000","0",	"0.1",	"3.0"},
		
		{new Boolean(false),"4019","100","0","1000","0",	"1.1",	"3.0"},
		{new Boolean(false),"4020","100","0","1000","0",	"1.1",	"3.0"},
		{new Boolean(false),"4021","100","0","1000","0",	"1.1",	"3.0"},
	
		{new Boolean(false),"4022","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"4023","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"4024","100","0","1000","0",	"0.1",	"3.0"},
		
		{new Boolean(false),"5025","100","0","1000","0",	"1.1",	"3.0"},
		{new Boolean(false),"5026","100","0","1000","0",	"1.1",	"3.0"},
		{new Boolean(false),"5027","100","0","1000","0",	"1.1",	"3.0"},
		
		{new Boolean(false),"5028","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"5029","100","0","1000","0",	"0.1",	"3.0"},
		{new Boolean(false),"5030","100","0","1000","0",	"1.1",	"3.0"},
		
	};
	
	//------------------------------------------------------
	//　テーブルモデル
	public class CustomTableModel extends DefaultTableModel {
		private static final long serialVersionUID = 1L;
	    CustomTableModel(Object[][] tableData, String[] tableColumnName) {
			super(tableData,tableColumnName);
		}

		public Class<?> getColumnClass(int col){
			return TableData[0][col].getClass();
	    }

	}

	
	class ActTransControl{
		public long time;
		public byte[] dat;
	}
	private boolean ACTProtocol=false;
	private int ACTAckSize = 8;
	private dataGenetaror[] DataGen = new dataGenetaror[DATAGENETATOR_MAX];
	private boolean 	SetupEnable = false;
	private boolean 	InitPhase = true;
	//private Object[] TableColumnData;
	private JButton jButtonTableApply = null;
	private HashMap<Integer,byte[]> TransmitBuffer=new HashMap<Integer,byte[]>();  //  @jve:decl-index=0:
	private HashMap<Long,ActTransControl> TransmitBufferAct=new HashMap<Long,ActTransControl>();  //  @jve:decl-index=0:
	private DatagramSocket Rsocket = null;  //  @jve:decl-index=0:
	private DatagramSocket Ssocket = null;
	private int RetryCounter = 0;
	private JButton jButtonPutError = null;
	private int PutError=0;
	private int PutErrorCounter=0;
	private JTextField jTextFieldMessage2 = null;
	private int MyPort = -1;
	private int CurrentSeq=0;
	private JCheckBox jCheckBoxACTPtorocol = null;
	private JList jListMessage = null;
	private JScrollPane jScrollPaneMessage = null;
	private  DefaultListModel messageListModel=null;  //  @jve:decl-index=0:
	private int messageCounter=0;
	private Vector<String> messageVector = new Vector<String>();
	private int ActSendCounter=0;
	private int ActReSendCounter=0;
	private int ActAckCounter=0;
	private Timer TimerThread=null;
	private JCheckBox jCheckBoxPackMultiChannel = null;
	private void saveProperty(){
	 	  Properties  prop = new  Properties();
	   	  Point pt = getLocation();
	   	  String pos = new String();
	   	  pos = String.format("%d",pt.x);
	   	  prop.setProperty(FmtWindowPosX, pos);
	   	  pos = String.format("%d",pt.y);
	   	  prop.setProperty(FmtWindowPosY, pos);
	   	  prop.setProperty(FmtTarget, jTextFieldTergetIP.getText());
	   	  
		  for(int j=0;j<jTable.getRowCount();j++){
		   	  for(int i=0;i< jTable.getColumnCount();i++){
		   		  String key = String.format("Table[%d,%d]",j,i);
		   		  Object dat = jTable.getValueAt(j,i);
		   		  if(dat instanceof String){
			   		  prop.setProperty(key, (String)dat);
		   		  }
		   		  else if(dat instanceof Boolean){
		   			  if((Boolean)dat){
		   				prop.setProperty(key, "y");  
		   			  }
		   			  else{
			   			prop.setProperty(key, "n");  
		   			  }
		   		  }
		   	  }
	   	  }
	   	  
	   	  try {
	          OutputStream stream = new FileOutputStream(PropertyFile);
	          prop.storeToXML(stream, "winA0sender", "Shift_JIS");
	          stream.close();
	      } catch (IOException ex) {
	          ex.printStackTrace();
	      }
				
	}

	private void loadProperty() {
		Properties prop = new Properties();
		try {
			InputStream stream = new FileInputStream(PropertyFile);
			prop.loadFromXML(stream);
			stream.close();
			Point pt = new Point();
			String pos1st = new String();
			pos1st = prop.getProperty(FmtWindowPosX);
			pt.x = Integer.parseInt(pos1st);
			pos1st = prop.getProperty(FmtWindowPosY);
			pt.y = Integer.parseInt(pos1st);
			this.setLocation(pt);
			String tmp = new String();
			tmp = prop.getProperty(FmtTarget);
			jTextFieldTergetIP.setText(tmp);
			  for(int j=0;j<jTable.getRowCount();j++){
			   	  for(int i=0;i< jTable.getColumnCount();i++){
			   		  String key = String.format("Table[%d,%d]",j,i);
			   		  String dat = prop.getProperty(key);
			   		  if(i==0) {
			   			  if(dat.compareToIgnoreCase("y")==0){
			   				jTable.setValueAt(true, j, i);
			   			  }
			   			  else{
				   			jTable.setValueAt(false, j, i);
			   			  }
			   		  }
			   		  else{
			   			  jTable.setValueAt(dat, j, i);
			   		  }
			   		  
			   	  }
		   	  }

		} catch (FileNotFoundException e) {
		} catch (IOException ex) {
			ex.printStackTrace();
		} catch (NullPointerException nl) {
			nl.printStackTrace();
		}

	}

	private void setMessage(String mes){
		jTextFieldMessage.setText(mes);
	}
	private void setMessage2(String mes){
		jTextFieldMessage2.setText(mes);
	}
	/**
	 * This method initializes jTextFieldTergetIP	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJTextFieldTergetIP() {
		if (jTextFieldTergetIP == null) {
			jTextFieldTergetIP = new JTextField();
			jTextFieldTergetIP.setBounds(new Rectangle(15, 30, 375, 31));
		}
		return jTextFieldTergetIP;
	}
	
	private synchronized void setDataGeneratorParameters(){
		//TableModel.getColumnCount()
		
		for(int i=0;i<jTable.getRowCount();i++){
			//{"act","name","smpl","mode","amp","dc-ofs","freq","phase};
			try{
				String str = new String();
				str = (String)jTable.getValueAt(i, 3);
				DataGen[i].Type = Integer.parseInt(str);
				str = (String)jTable.getValueAt(i, 4);
				DataGen[i].Amplitude = Double.parseDouble(str);
				str = (String)jTable.getValueAt(i, 5);
				DataGen[i].Center = Double.parseDouble(str);
				str = (String)jTable.getValueAt(i, 6);
				DataGen[i].Freq = Double.parseDouble(str);
				if(InitPhase){
					str = (String)jTable.getValueAt(i, 7);
					DataGen[i].Phase = Double.parseDouble(str);
				}
			} catch(NumberFormatException en){
				System.out.println(en);
			}
		}
		InitPhase = false;
	}
	/**
	 * This method initializes jToggleButtonSend	
	 * 	
	 * @return javax.swing.JToggleButton	
	 */
	private void startSend(){
		if(!SetupEnable){
			setMessage(jTextFieldTergetIP.getText());
			String ips[] = jTextFieldTergetIP.getText().split(":");
			if(ips.length != 2) {
				setMessage("Error format IP:PORT");
				jToggleButtonSend.setSelected(false);
				return;
			}
			TargetIp = new String(ips[0]);
			TargetPort = Integer.parseInt(ips[1]);
			setDataGeneratorParameters();
			SetupEnable=true;
		}		
	}
	private void stopSend(){
		if(SetupEnable){
			SendCounter=0;
			SendCounterLong=0L;
			SetupEnable=false;
		}
	}
	private  JToggleButton getJToggleButtonSend() {
		if (jToggleButtonSend == null) {
			jToggleButtonSend = new JToggleButton();
			jToggleButtonSend.setBounds(new Rectangle(397, 33, 76, 26));
			jToggleButtonSend.setText("SEND");
			jToggleButtonSend.addChangeListener(new javax.swing.event.ChangeListener() {
				public void stateChanged(javax.swing.event.ChangeEvent e) {
					if(e.getSource().equals(jToggleButtonSend)){
						if(jToggleButtonSend.isSelected()){
							startSend();
						}
						else{
							stopSend();
						}
					}
				}
			});
		}
		return jToggleButtonSend;
	}

	/**
	 * This method initializes jTextFieldMessage	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJTextFieldMessage() {
		if (jTextFieldMessage == null) {
			jTextFieldMessage = new JTextField();
			jTextFieldMessage.setBounds(new Rectangle(15, 64, 462, 24));
		}
		return jTextFieldMessage;
	}

	/**
	 * This method initializes jScrollPane	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getJScrollPane() {
		if (jScrollPane == null) {
			jScrollPane = new JScrollPane();
			jScrollPane.setToolTipText("mode: 0=Sin,1=Squar,2=Sawtooth,3=Impulse,4=Random,5=Const\r\n");
			jScrollPane.setBounds(new Rectangle(7, 201, 469, 217));
			jScrollPane.setViewportView(getJTable());
		}
		return jScrollPane;
	}

	/**
	 * This method initializes jTable	
	 * 	
	 * @return javax.swing.JTable	
	 */
	private JTable getJTable() {
		if (jTable == null) {
		    //DefaultTableModel TableModel = new DefaultTableModel(TableData,TableColumnName);
		    CustomTableModel TableModel = new CustomTableModel(TableData,TableColumnName);
			jTable = new JTable(TableModel);
			jTable.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
				public void propertyChange(java.beans.PropertyChangeEvent e) {
					if ((e.getPropertyName().equals("model"))) {
						System.out.println("propertyChange(model)"); 
					}
				}
			});
		}
		return jTable;
	}

	/**
	 * This method initializes jButtonTableApply	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJButtonTableApply() {
		if (jButtonTableApply == null) {
			jButtonTableApply = new JButton();
			jButtonTableApply.setBounds(new Rectangle(399, 130, 73, 24));
			jButtonTableApply.setText("Apply");
			jButtonTableApply.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					InitPhase = true;
					setDataGeneratorParameters();
				
				}
			});
		}
		return jButtonTableApply;
	}

	/**
	 * This method initializes jButtonPutError	
	 * 	
	 * @return javax.swing.JButton	
	 */
	private JButton getJButtonPutError() {
		if (jButtonPutError == null) {
			jButtonPutError = new JButton();
			jButtonPutError.setBounds(new Rectangle(20, 130, 84, 24));
			jButtonPutError.setText("PacLoss");
			jButtonPutError.addActionListener(new java.awt.event.ActionListener() {
				public void actionPerformed(java.awt.event.ActionEvent e) {
					PutError++;
				}
			});
		}
		return jButtonPutError;
	}

	/**
	 * This method initializes jTextFieldMessage2	
	 * 	
	 * @return javax.swing.JTextField	
	 */
	private JTextField getJTextFieldMessage2() {
		if (jTextFieldMessage2 == null) {
			jTextFieldMessage2 = new JTextField();
			jTextFieldMessage2.setBounds(new Rectangle(16, 91, 460, 28));
		}
		return jTextFieldMessage2;
	}

	/**
	 * This method initializes jCheckBoxACTPtorocol	
	 * 	
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getJCheckBoxACTPtorocol() {
		if (jCheckBoxACTPtorocol == null) {
			jCheckBoxACTPtorocol = new JCheckBox();
			jCheckBoxACTPtorocol.setBounds(new Rectangle(267, 132, 124, 24));
			jCheckBoxACTPtorocol.setText("ACT protocol");
			jCheckBoxACTPtorocol.addItemListener(new java.awt.event.ItemListener() {
				public void itemStateChanged(java.awt.event.ItemEvent e) {
					if(jToggleButtonSend.isSelected()){
						stopSend();
					}
					ACTProtocol = jCheckBoxACTPtorocol.isSelected();
					if(jToggleButtonSend.isSelected()){
						startSend();
					}
				}
			});
		}
		return jCheckBoxACTPtorocol;
	}

	/**
	 * This method initializes jListMessage	
	 * 	
	 * @return javax.swing.JList	
	 */
	private JList getJListMessage() {
		if (jListMessage == null) {
			jListMessage = new JList();
			messageListModel = new DefaultListModel();
			jListMessage.setModel(messageListModel);
		}
		return jListMessage;
	}

	/**
	 * This method initializes jScrollPaneMessage	
	 * 	
	 * @return javax.swing.JScrollPane	
	 */
	private JScrollPane getJScrollPaneMessage() {
		if (jScrollPaneMessage == null) {
			jScrollPaneMessage = new JScrollPane();
			jScrollPaneMessage.setBounds(new Rectangle(5, 424, 470, 113));
			jScrollPaneMessage.setViewportView(getJListMessage());
		}
		return jScrollPaneMessage;
	}

	/**
	 * This method initializes jCheckBoxPackMultiChannel	
	 * 	
	 * @return javax.swing.JCheckBox	
	 */
	private JCheckBox getJCheckBoxPackMultiChannel() {
		if (jCheckBoxPackMultiChannel == null) {
			jCheckBoxPackMultiChannel = new JCheckBox();
			jCheckBoxPackMultiChannel.setBounds(new Rectangle(23, 166, 157, 23));  // Generated
			jCheckBoxPackMultiChannel.setText("Pack multi channel");  // Generated
		}
		return jCheckBoxPackMultiChannel;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new WinSender(null);
	}

	/**
	 * @param owner
	 */
	public WinSender(Frame owner) {
		super(owner);
		setTitle("win sender  - win A0 / ACT packet sender  Hakusan/T.ikeda");
		initialize();
		this.setVisible(true);

		this.addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent ev) {
				saveProperty();
				System.exit(0);
			}
		});
	}

	/**
	 * This method initializes this
	 * 
	 * @return void
	 */
	private void initialize() {
		this.setSize(500, 583);
		this.setContentPane(getJContentPane());
		this.setLocationByPlatform(true);
		loadProperty();
		Calendar cal = Calendar.getInstance();
		int h = cal.get(Calendar.HOUR);
		int ap = cal.get(Calendar.AM_PM);
		if(ap!=0) h+=12;
		CurrentTime = new wintime(cal.get(Calendar.YEAR),cal.get(Calendar.MONTH)+1,cal.get(Calendar.DAY_OF_MONTH),h,cal.get(Calendar.MINUTE),cal.get(Calendar.SECOND));
		
		for(int i=0;i<DATAGENETATOR_MAX;i++){
			DataGen[i] = new dataGenetaror();
		}
		Timer TimerThread = new Timer();
		TimerThread.schedule(new cTimer(),0,1000);
		
		try{
			MyPort=10000;
			Ssocket = new DatagramSocket(MyPort);
			//Ssocket.bind(null);
			//Rsocket = new  DatagramSocket();
			//Rsocket.setSoTimeout(10);
			Ssocket.setSoTimeout(10);
			
		}catch(SocketException e){
			e.printStackTrace();
		}
		CRCAct = new Crc(0xd008,false); // ACT protocol
		//CRCAct = new Crc(0x100b,true); // ACT protocol
		//CRCAct = new Crc(0x1021);	// CRC-CCITT
	}
//	private int[] makeRawData(int length){
//		int[] ret = new int[length];
//		double phase=2*3.1415/length * 10;
//		double amp = 1000.0;
//		for(int i=0;i<length;i++){
//			ret[i] = (int)(amp * Math.sin(phase*i));
// 		}
//		return ret;
//	}
	
	private byte[] makeEncodeData(int chidx,int channelID,int length){
		//int[] rd = makeRawData(length);
		int[] rd = DataGen[chidx].makeRawData(length);
		int max = 0x80000000;
		int min = 0x7fffffff;
		for(int i=0;i<rd.length-1;i++){
			int delta = rd[i+1]-rd[i];
			if(max < delta) max = delta;
			if(min > delta) min = delta;
		}
//		for(int i=0;i<rd.length;i++){
//			int delta = rd[i];
//			if(max < delta) max = delta;
//			if(min > delta) min = delta;
//		}
		int enc ;
		if(max > 0x7fffff || min < -0x800000)  enc = 4;
		else if(max > 0x7fff || min < -0x8000) enc = 3;
		else if(max > 0x7f || min < -0x80) enc = 2;
		else if(max > 0x7 || min < -0x8) enc = 1;
		else enc = 0;
		int dlength;
		if(enc == 0) dlength = 4 + (rd.length) / 2;
		else         dlength = 4 + (rd.length -1)*enc;
		
		
		byte[] ret = new byte[dlength+4];
		byteUtil.conv2UByte(channelID,ret,0);
		int encandlen = (rd.length & 0xfff) | (enc<<12) & 0xf000;
		byteUtil.conv2UByte(encandlen,ret,2);
		
		int index = 0;
		byteUtil.conv4Byte(rd[index],ret,4);
		index++;
		for(int bi=4+4;index<rd.length;index++){
			switch(enc){
			case 4: 
				byteUtil.conv4Byte(rd[index] - rd[index-1], ret, bi);
				bi += 4;
				break;
			case 3: 
				byteUtil.conv3Byte(rd[index] - rd[index-1], ret, bi);
				bi += 3;
				break;
			case 2: 
				byteUtil.conv2Byte(rd[index]- rd[index-1], ret, bi);
				bi += 2;
				break;
			case 1: 
				byteUtil.convByte(rd[index]  - rd[index-1], ret, bi);
				bi += 1;
				break;
			case 0: 
				byteUtil.convHalfByte(rd[index]  - rd[index-1], ret, bi,false);
				index++;
				if(rd.length>index) byteUtil.convHalfByte(rd[index]  - rd[index-1], ret, bi,true);
				bi += 1;
				break;
			}
		}
		return ret;
	}
	
	private byte[] makeWinData(byte[] header,int chidx,int channenl,int length,wintime sendtime,int tail,boolean winA0){
		byte[] tm = sendtime.getBcd();
		
		byte[] ch = makeEncodeData(chidx,channenl,length);
		byte[] le = byteUtil.conv2UByte(ch.length+tm.length+2);
		
		int hdlen = 0;
		if(header!=null){
			hdlen = header.length;
		}
		byte[] winframe = new byte[hdlen+le.length+tm.length+ch.length+tail];
		
		int index=0;
		if(header!=null){
			for(int i=0;i<header.length;i++){
				winframe[index++] = header[i];
			}
		}
		for(int i=0;i<le.length;i++){
			winframe[index++] = le[i];
		}
		for(int i=0;i<tm.length;i++){
			winframe[index++] = tm[i];
		}
		for(int i=0;i<ch.length;i++){
			winframe[index++] = ch[i];
		}
		return winframe;
		
	}
	private byte[] makeWinFrame(boolean udpheader,int chidx,int channenl,int length,wintime sendtime){
		byte[] header = null;
		if(udpheader){
			header = new byte[3];
			header[1] = header[0] = byteUtil.convUByte(SendCounter&0xff);
			//System.out.println(String.format("SendSeq=%d,%d",byteUtil.convShort(header[0]),byteUtil.convShort(header[1])));
			header[2] = (byte)0xa0;
		}
		return makeWinData(header,chidx,channenl,length,sendtime,0,true);
	}
	
	private int calcCRC(byte[] dat,int length){
		long crc = CRCAct.calc(dat,length);
		
		return (int)(crc & 0xffff);
	}
	
	private byte[] makeACTFrame(boolean udpheader,int chidx,int channenl,int length,wintime sendtime){
		byte[] header = null;
		if(udpheader){
			header = new byte[18+3];
		}
		else{
			header = new byte[18];
		}
		ActProtocolFrame.setActHeader(header,0);
		ActProtocolFrame.setSeq(header,0,SendCounterLong);
		ActProtocolFrame.setAckSize(header,0,ACTAckSize);
		ActProtocolFrame.setDataType(header,0,0x00a0);
//		byte test[] = new byte[1];
//		test[0] = '1'; // -- CCITT / "1" => 0x2672(9842)
//		//test[1] = '2';
//		//test[2] = '3';
//		//test[3] = '4'; // -- CCITT / "1234" => 0xd789(55177) 
//		int testCrc=calcCRC(test,test.length);
		if(udpheader){
			header[18] = header[19] = byteUtil.convUByte(SendCounter&0xff);
			header[20] = (byte)0xa0;
		}
		byte windat[] =  makeWinData(header,chidx,channenl,length,sendtime,2,false);
		ActProtocolFrame.setDataLength(windat,0,windat.length-20);
		int crc16 = calcCRC(windat,windat.length-2);
		ActProtocolFrame.setDataCRC(windat,0,crc16);
//		windat[windat.length-2] = (byte)((crc16 >> 8) & 0xff);
//		windat[windat.length-1] = (byte)((crc16 >> 0) & 0xff);
		return windat;
	}
	
	private synchronized void onTimer()  {
		
		CurrentTime.inc_sec();
		
		if(SetupEnable){
			if(jCheckBoxPackMultiChannel.isSelected()){
				lanSendPack();
			}
			else{
				lanSendNormal();
			}
			lanRecv();
		}
		
	}

	private void lanRecv() {
		while(true){
			byte [] rb = new byte[1000];
			DatagramPacket rpac = new DatagramPacket(rb,rb.length);
			try{
				Ssocket.receive(rpac);
				if(rpac.getLength()>0){
					if(ACTProtocol){
						RecvcheckAck(rb,rpac);
					}
					else{
						RecvcheckReSend(rb, rpac);
					}
				}
			}catch(SocketTimeoutException e){
				break;
			}catch(IOException e){
				e.printStackTrace();
			}
		}
		ActRetransmit();
	}
	/**
	 * 
	 */
	private void lanSendPack() {
		try {
			InetSocketAddress adr;
			adr = new InetSocketAddress(TargetIp, TargetPort);
		
			wintime sendTime = new wintime(CurrentTime);
			//{"act","name","smpl","mode","amp","dc-ofs","freq","phase};
			
			ArrayList<byte[]> dlist = new ArrayList<byte[]>();
			int dsize = 0;
			try{
				for(int i=0;i<jTable.getRowCount();i++){
					Boolean active= (Boolean)jTable.getValueAt(i, 0);
					if(active){
						String str;
						str = (String)jTable.getValueAt(i, 1);
						int ch = Integer.parseInt(str,16);					
						str = (String)jTable.getValueAt(i, 2);
						int le = Integer.parseInt(str);					
						byte[] data = null;
						data = makeWinFrame(false,i,ch,le,sendTime);
						dsize += data.length;
						dlist.add(data);
					}
				}
				byte[] data = new byte[dsize];
				int index = 0;
				for(int i=0;i<dlist.size();i++){
					byte[] d = dlist.get(i);
					for(int j=0;j<d.length;j++){
						data[index++]=d[j];
					}
				}
				byte[] data2 = null;
				byte[] header = new byte[3];
				header[1] = header[0] = byteUtil.convUByte(SendCounter&0xff);
				//System.out.println(String.format("SendSeq=%d,%d",byteUtil.convShort(header[0]),byteUtil.convShort(header[1])));
				header[2] = (byte)0xa0;
				
				data2 = new byte[data.length+3];
				System.arraycopy(header, 0, data2, 0, header.length);
				System.arraycopy(data, 0, data2,header.length , data.length);
				if(ACTProtocol){
					return;
					// no support
				}
				else{
					Integer seq = new Integer(data2[0]);
					if(seq < 0) seq += 0x100;
					CurrentSeq=seq;
					TransmitBuffer.put(seq, data2);
					if(PutError != 0){
						PutError--;
						//System.out.print(String.format("LOS%d Seq=%d\n",SendCounter,seq));
						display(String.format("Pressed PacLoss(winA0)  %d \n",CurrentSeq));
						PutErrorCounter++;
					}
					else{
						DatagramPacket tosend = new DatagramPacket(data2, data2.length, adr);
						Ssocket.send(tosend);
						if(data2.length > 1472){
							
						}
						this.wait(10);
					}
				}
				//System.out.println(String.format("PUT=%d",seq));
				String mes = new String();
				mes = String.format("%s (%s:%d) %dbyte ", CurrentTime.fmt(),TargetIp, TargetPort,
						data2.length );
				if(data2.length>1472){
					mes += " data len over!(>1472)";
				}
				setMessage(mes);
				mes = String.format("Snd=%d/Loss=%d/RetryRcv=%d ActSnd=%d/ActAck=%d/ActReSnd=%d", SendCounter,PutErrorCounter,RetryCounter,ActSendCounter,ActAckCounter,ActReSendCounter);
				SendCounter++;
				SendCounterLong++;
				
				setMessage2(mes);
			}catch(NumberFormatException e){
				System.out.println(e);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
			jToggleButtonSend.setSelected(false);
			setMessage("Unknown host");
		} catch (IOException e) {
			e.printStackTrace();
			jToggleButtonSend.setSelected(false);
			setMessage("Fail to send");
		}
	}

	private void lanSendNormal() {
		try {
			InetSocketAddress adr;
			adr = new InetSocketAddress(TargetIp, TargetPort);
		
			wintime sendTime = new wintime(CurrentTime);
			//{"act","name","smpl","mode","amp","dc-ofs","freq","phase};
			for(int i=0;i<jTable.getRowCount();i++){
				try{
				Boolean active = (Boolean)jTable.getValueAt(i, 0);
				if(active){
					String str;
					str = (String)jTable.getValueAt(i, 1);
					int ch = Integer.parseInt(str,16);					
					str = (String)jTable.getValueAt(i, 2);
					int le = Integer.parseInt(str);					
					byte[] data;
					if(ACTProtocol){
						data = makeACTFrame(true,i,ch,le,sendTime);
						long sq = SendCounterLong;
						ActTransControl ac = new ActTransControl();
						ac.dat = data;
						ac.time = System.currentTimeMillis();
						TransmitBufferAct.put(sq, ac);
						if(PutError != 0){
							PutError--;
							//System.out.print(String.format("LOS%d Seq=%d\n",SendCounter,sq));
							//display(String.format("ACT:LOS%d Seq=%d\n",SendCounter,sq));
							display(String.format("Pressed PacLoss(ACT)  %d \n",sq));
							PutErrorCounter++;
						}
						else{
							//System.out.println(String.format("send:%02x %02x %02x %02x %02x %02x %02x %02x" , 
							//		data[4],data[5],data[6],data[7],data[8],data[9],data[10],data[11]));
		
							ActSendCounter++;
							DatagramPacket tosend = new DatagramPacket(data, data.length, adr);
							Ssocket.send(tosend);
							this.wait(10);
						}
					}
					else{
						data = makeWinFrame(true,i,ch,le,sendTime);
						Integer seq = new Integer(data[0]);
						if(seq < 0) seq += 0x100;
						CurrentSeq=seq;
						TransmitBuffer.put(seq, data);
						if(PutError != 0){
							PutError--;
							//System.out.print(String.format("LOS%d Seq=%d\n",SendCounter,seq));
							display(String.format("Pressed PacLoss(winA0)  %d \n",CurrentSeq));
							PutErrorCounter++;
						}
						else{
							DatagramPacket tosend = new DatagramPacket(data, data.length, adr);
							Ssocket.send(tosend);
							this.wait(10);
						}
					}
					//System.out.println(String.format("PUT=%d",seq));
					String mes = new String();
					mes = String.format("%s %s:%d %dbyte ", CurrentTime.fmt(),TargetIp, TargetPort,
							data.length );
					setMessage(mes);
					mes = String.format("Snd=%d/Loss=%d/RetryRcv=%d ActSnd=%d/ActAck=%d/ActReSnd=%d", SendCounter,PutErrorCounter,RetryCounter,ActSendCounter,ActAckCounter,ActReSendCounter);
					SendCounter++;
					
					SendCounterLong++;
					
					setMessage2(mes);
						
				}
				}catch(NumberFormatException e){
					System.out.println(e);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		} catch (UnknownHostException e1) {
			e1.printStackTrace();
			jToggleButtonSend.setSelected(false);
			setMessage("Unknown host");
		} catch (IOException e) {
			e.printStackTrace();
			jToggleButtonSend.setSelected(false);
			setMessage("Fail to send");
		}
	}
	// ACT 時刻をみてACKの無いパケットを再送する。
	private void ActRetransmit(){
		long now = System.currentTimeMillis();
		Set<Long> set = TransmitBufferAct.keySet();
		Iterator<Long> ite = set.iterator();
		while(ite.hasNext()){
			long seq=ite.next();
			ActTransControl ac = TransmitBufferAct.get(seq);
			if(now - ac.time > 10000L){
				try{
					InetSocketAddress adr2 = new InetSocketAddress(TargetIp, TargetPort);
					DatagramPacket tosend = new DatagramPacket(ac.dat, ac.dat.length, adr2);
					Ssocket.send(tosend);
					ac.time=now;
					TransmitBufferAct.put(seq, ac);
					SendCounter++;
					ActReSendCounter++;
					display(String.format("ACT resend: Seq=%x \n",seq));
				}
				catch(SocketException e){
					e.printStackTrace();
				}
				catch(IOException e){
					e.printStackTrace();
				}
			}
		}
//		Iterator<Long> ite2 = del.iterator();
//		while(ite2.hasNext()){
//			long seq = ite2.next();
//			TransmitBufferAct.remove(seq);
//		}
		
	}
	// ACTの場合、ACKパケットで送信バッファから消す
	private void RecvcheckAck(byte[] rb, DatagramPacket rpac) throws SocketException, IOException {
		//int paclen = rb.length;
		int paclen = rpac.getLength();
		int rbindex=0;
		while(paclen>0){
			if(paclen<32) break;
			byte[] dat = new byte[32];
			for(int i=0;i<32;i++){
				dat[i] = rb[rbindex++];
				paclen--;
			}
			if(ActProtocolFrame.isActHeader(dat,0)){
				if(ActProtocolFrame.isActAck(dat,0)){
					int crcc = calcCRC(dat,dat.length-2);
					int crcr = ActProtocolFrame.getDataCRC(dat,0);
					if(crcc == crcr){
						//System.out.println(String.format("ack:%02x %02x %02x %02x %02x %02x %02x %02x - %02x %02x %02x %02x" , 
						//		dat[18],dat[19],dat[20],dat[21],dat[22],dat[23],dat[24],dat[25], dat[26],dat[27],dat[28],dat[29] ));
						TreeSet<Long> ts = ActProtocolFrame.getAckNumbers(dat,0);
						Iterator<Long> ite = ts.iterator();
						while(ite.hasNext()){
							long seq=ite.next();
							TransmitBufferAct.remove(seq);
							ActAckCounter++;
						}
					}
					else{
						display(String.format("ACT ack CRC Error: %x/%x \n",crcc,crcr));
					}
				}
				else{
					display(String.format("ACT recv not ack:\n"));
				}
			}
			else{
				display(String.format("ACT not ACT Header: %02x %02x %02x %02x\n",dat[0],dat[1],dat[2],dat[3]));
			}
		}
		for(int i=0;i<rpac.getLength();i++){
		
		}
	}
	// win A0の場合、NACKパケット受信で再送する
	private void RecvcheckReSend(byte[] rb, DatagramPacket rpac) throws SocketException, IOException {
		for(int i=0;i<rpac.getLength();i++){
			Integer seq = new Integer(rb[i]);
			if(seq <= 0) seq += 0x100;
			int delta;
			if(seq < CurrentSeq) delta = CurrentSeq -seq;
			else				delta = CurrentSeq - seq + 0x100;
			
			//System.out.print(String.format("NACK: RecSeq=%d CurrentSeq=%d delta=%d\n",seq,CurrentSeq,delta));
			display(String.format("NACK: RecSeq=%d CurrentSeq=%d delta=%d\n",seq,CurrentSeq,delta));
			
			if(delta < 128){
				byte[] retry = TransmitBuffer.get(seq);
				if(retry!=null){
					// 再送パケット送信
					// [0] 通常のシーケンス番号を連続させる
					// [1] 再送されるシーケンス番号
					retry[0] = byteUtil.convUByte(SendCounter);
					//System.out.print(String.format("Snd Retry Seq=%d,%d\n",byteUtil.convShort(retry[0]),byteUtil.convShort(retry[1])));
					display(String.format(" Retry Seq=%d,%d\n",byteUtil.convShort(retry[0]),byteUtil.convShort(retry[1])));
					RetryCounter++;
					InetSocketAddress adr2 = new InetSocketAddress(TargetIp, TargetPort);
					DatagramPacket tosend = new DatagramPacket(retry, retry.length, adr2);
					Ssocket.send(tosend);
					SendCounter++;
				}
			}
		}
	}
	/**
	 * This method initializes jContentPane
	 * 
	 * @return javax.swing.JPanel
	 */
	private JPanel getJContentPane() {
		if (jContentPane == null) {
			jLabel = new JLabel();
			jLabel.setBounds(new Rectangle(15, 0, 459, 31));
			jLabel.setText("IP:PORT (example. 127.0.0.1:2701)");
			jContentPane = new JPanel();
			jContentPane.setLayout(null);
			jContentPane.add(getJTextFieldTergetIP(), null);
			jContentPane.add(getJToggleButtonSend(), null);
			jContentPane.add(jLabel, null);
			jContentPane.add(getJTextFieldMessage(), null);
			jContentPane.add(getJScrollPane(), null);
			jContentPane.add(getJButtonTableApply(), null);
			jContentPane.add(getJButtonPutError(), null);
			jContentPane.add(getJTextFieldMessage2(), null);
			jContentPane.add(getJCheckBoxACTPtorocol(), null);
			jContentPane.add(getJScrollPaneMessage(), null);
			jContentPane.add(getJCheckBoxPackMultiChannel(), null);
		}
		return jContentPane;
	}
	public class cTimer extends TimerTask{
		  public void run(){
				onTimer();
		  }
	}
	
	
	class dataGenetaror{
		//private double CPhase=0.0;
		public double Amplitude = 100.0;
		public double Phase = 0.0;
		public double Freq = 0.0;
		public int Length = 1000;
		public double Center = 0.0;
		public int  Type;
		private int flag = 0;
		
		public int[] makeRawData(int length){
			int[] ret = new int[length];
			Length = length;
			//System.out.println( String.format("> %f ",CPhase));
			double delta = 2*Math.PI / length * Freq; 
			for(int i=0;i<length;i++){
				//Phase += delta;
				if(Phase > Math.PI) Phase -= 2*Math.PI;
				if(Phase < -Math.PI) Phase += 2*Math.PI;
				if(Type == 0){
					ret[i] = (int)(Center + Amplitude * Math.sin(Phase));
					//System.out.println( String.format("%d",ret[i]));
				}
				else if(Type==1){
					if(Math.sin(Phase) >0) ret[i] = (int)(Center+Amplitude);
					else 					ret[i] = (int)(Center-Amplitude);
				}
				else if(Type==2){
					double ofs = Amplitude * Phase/Math.PI; 
					ret[i] = (int)(Center + ofs);
				}
				else if(Type==3){
					if((Math.sin(Phase) >0.5) && flag == 0){
						ret[i] = (int)(Center+Amplitude);
						flag = 1;
					}
					else{
						ret[i] = (int)(Center);
						if(Math.sin(Phase) < -0.5) flag = 0;	
					}
				}
				else if(Type==4){
					double ofs = Math.random() - 0.5; 
					ret[i] = (int)(Center + 2.0*ofs*Amplitude);
				}
				else{
					ret[i] = (int)(Center);
				}
				Phase += delta;
	 		}
			//System.out.println( String.format("< %f %f",CPhase,CPhase-delta));
			return ret;
		}
	}
	private  void display(String mes){
		synchronized(messageVector ){
			messageVector.add(String.format("%04d:",messageCounter++)+mes);
		}
		SwingUtilities.invokeLater(new Runnable() {
	        @Override
            public void run() {
	    		synchronized(messageVector ){
		            for(int i=0;i<messageVector.size();i++){
			        	messageListModel.addElement(messageVector.get(i));
		            }
		            messageVector.clear();
		            while(messageListModel.size()>50){
	    				messageListModel.remove(0);
	    			}
	    		}
            }
	        });
		//jListMessage.scrollRectToVisible(getBounds())
	}
}  //  @jve:decl-index=0:visual-constraint="10,10"
